import { TrackOpTypes, TriggerOpTypes } from "./operations";
import { extend, isArray, isIntegerKey, isMap } from "@vue/shared";
import { EffectScope, recordEffectScope } from "./effectScope";
import {
  createDep,
  Dep,
  finalizeDepMarkers,
  initDepMarkers,
  newTracked,
  wasTracked,
} from "./dep";

// The main WeakMap that stores {target -> key -> dep} connections.
/**
 * {
 *  target:{key:dep}
 * }
 */
// Conceptually, it's easier to think of a dependency as a Dep class
// which maintains a Set of subscribers, but we simply store them as
// raw Sets to reduce memory overhead.
type KeyToDepMap = Map<any, Dep>;
/**
 * targetMap: 存储
 *  target 和 deps依赖 的映射
 *  {
 *    target:{
 *      key:[]
 *    }
 *  }
 */
const targetMap = new WeakMap<any, KeyToDepMap>();

// The number of effects currently being tracked recursively.
let effectTrackDepth = 0;

export let trackOpBit = 1;

/**
 * The bitwise track markers support at most 30 levels op recursion.
 * This value is chosen to enable modern JS engines to use a SMI on all platforms.
 * When recursion depth is greater, fall back to using a full cleanup.
 */
const maxMarkerBits = 30;

export type EffectScheduler = (...args: any[]) => any;

export type DebuggerEvent = {
  effect: ReactiveEffect;
} & DebuggerEventExtraInfo;

export type DebuggerEventExtraInfo = {
  target: object;
  type: TrackOpTypes | TriggerOpTypes;
  key: any;
  newValue?: any;
  oldValue?: any;
  oldTarget?: Map<any, any> | Set<any>;
};

/**
 * // 副作用函数 -- 这里是为了暂时的存放每一个副作用函数，
 * 当副作用函数已经和依赖关系建立好了之后再清除空间
 */
const effectStack: ReactiveEffect[] = [];
let activeEffect: ReactiveEffect | undefined;

export const ITERATE_KEY = Symbol(__DEV__ ? "iterate" : "");
export const MAP_KEY_ITERATE_KEY = Symbol(__DEV__ ? "Map key iterate" : "");

// fn函数包装成需要用到 effect函数
export class ReactiveEffect<T = any> {
  active = true;
  deps: Dep[] = [];

  // can be attached after creation
  computed?: boolean;
  allowRecurse?: boolean;
  onStop?: () => void;
  // dev only
  onTrack?: (event: DebuggerEvent) => void;
  // dev only
  onTrigger?: (event: DebuggerEvent) => void;

  constructor(
    public fn: () => T,
    public scheduler: EffectScheduler | null = null,
    scope?: EffectScope | null
  ) {
    recordEffectScope(this, scope);
  }

  run() {
    if (!this.active) {
      return this.fn();
    }
    if (!effectStack.includes(this)) {
      try {
        /**
         * 先存起来 再将 activeEffect 赋值给这个包装的对象
         *  之后track的时候是收集的target和这个 activeEffect
         *
         *  所以trigger的时候 里面的对象其实就是一个个的 activeEffect
         *   trigger执行的时候是执行 trigger.run() 等同于执行了 fn()
         */
        effectStack.push((activeEffect = this));
        enableTracking();

        trackOpBit = 1 << ++effectTrackDepth;

        if (effectTrackDepth <= maxMarkerBits) {
          initDepMarkers(this);
        } else {
          cleanupEffect(this);
        }
        /**
         * 为什么要先将值存进 effectStack 再去掉 effectStack的这个值？
         *  再执行函数之前先将副作用存进 effectStack 这个数组
         *  再执行 fn()
         *  关键在于执行fn()的时候 会去将 effectStack 里面的值存进 targetMap里面
         *  所以当函数执行结束的时候 再将这个数组中的值给进行清空 方便下次再进行操作
         *
         *  所以这里的 effectStack 相当于是 副作用函数的中间临时存放点
         */
        return this.fn();
      } finally {
        if (effectTrackDepth <= maxMarkerBits) {
          finalizeDepMarkers(this);
        }

        trackOpBit = 1 << --effectTrackDepth;

        resetTracking();
        // 再清空掉这个值
        effectStack.pop();
        const n = effectStack.length;
        activeEffect = n > 0 ? effectStack[n - 1] : undefined;
      }
    }
  }

  stop() {
    if (this.active) {
      cleanupEffect(this);
      if (this.onStop) {
        this.onStop();
      }
      this.active = false;
    }
  }
}

function cleanupEffect(effect: ReactiveEffect) {
  const { deps } = effect;
  if (deps.length) {
    for (let i = 0; i < deps.length; i++) {
      deps[i].delete(effect);
    }
    deps.length = 0;
  }
}

export interface DebuggerOptions {
  onTrack?: (event: DebuggerEvent) => void;
  onTrigger?: (event: DebuggerEvent) => void;
}

export interface ReactiveEffectOptions extends DebuggerOptions {
  lazy?: boolean;
  scheduler?: EffectScheduler;
  scope?: EffectScope;
  allowRecurse?: boolean;
  onStop?: () => void;
}

export interface ReactiveEffectRunner<T = any> {
  (): T;
  effect: ReactiveEffect;
}

/**
 * effect方法接收的 fn 就是我们需要存储到 deps中的 副作用函数
 */
export function effect<T = any>(
  fn: () => T,
  options?: ReactiveEffectOptions
): ReactiveEffectRunner {
  if ((fn as ReactiveEffectRunner).effect) {
    fn = (fn as ReactiveEffectRunner).effect.fn;
  }

  /**
   * 包装一下 fn 副作用函数
   *  当包装的 _effect对象执行了 run 方法之后，就完成了依赖收集的过程了
   *  具体看 ReactiveEffect 这个类中的 run方法
   */
  const _effect = new ReactiveEffect(fn);

  if (options) {
    extend(_effect, options);
    if (options.scope) recordEffectScope(_effect, options.scope);
  }
  if (!options || !options.lazy) {
    // 执行以下这个副作用函数
    _effect.run();
  }
  const runner = _effect.run.bind(_effect) as ReactiveEffectRunner;
  runner.effect = _effect;
  // 这里就理解成 返回了一个副作用函数出去
  return runner;
}

export function stop(runner: ReactiveEffectRunner) {
  runner.effect.stop();
}

let shouldTrack = true;
const trackStack: boolean[] = [];

export function pauseTracking() {
  trackStack.push(shouldTrack);
  shouldTrack = false;
}

export function enableTracking() {
  trackStack.push(shouldTrack);
  shouldTrack = true;
}

export function resetTracking() {
  const last = trackStack.pop();
  shouldTrack = last === undefined ? true : last;
}

/**
 * 建立 target和副作用之间的关系的方法
 */
export function track(target: object, type: TrackOpTypes, key: unknown) {
  if (!isTracking()) {
    return;
  }
  /**
   * 获取 deps 的值
   */
  let depsMap = targetMap.get(target);
  if (!depsMap) {
    // 集合中不存在 则添加上这个关系
    targetMap.set(target, (depsMap = new Map()));
  }
  let dep = depsMap.get(key);
  if (!dep) {
    // 判断映射中这个target对应的 dep 这个依赖集
    depsMap.set(key, (dep = createDep()));
  }
  /**
   * 到这一步 dep就肯定存在了 是存放 副作用函数的 集合
   */

  const eventInfo = __DEV__
    ? { effect: activeEffect, target, type, key }
    : undefined;

  trackEffects(dep, eventInfo);
}

export function isTracking() {
  return shouldTrack && activeEffect !== undefined;
}

export function trackEffects(
  dep: Dep,
  debuggerEventExtraInfo?: DebuggerEventExtraInfo
) {
  /**
   * 这里的工作就大致于是 将这个effect添加到这个dep依赖数组中
   *
   *  这里的effect 就是  activeEffect
   *    dep是set 使用 set.add()api将这个activeEffect添加到dep里面
   */
  let shouldTrack = false; // 是否添加到集合中
  if (effectTrackDepth <= maxMarkerBits) {
    if (!newTracked(dep)) {
      dep.n |= trackOpBit; // set newly tracked
      shouldTrack = !wasTracked(dep);
    }
  } else {
    // Full cleanup mode.
    shouldTrack = !dep.has(activeEffect!);
  }

  if (shouldTrack) {
    dep.add(activeEffect!);
    activeEffect!.deps.push(dep);
    if (__DEV__ && activeEffect!.onTrack) {
      activeEffect!.onTrack(
        Object.assign(
          {
            effect: activeEffect!,
          },
          debuggerEventExtraInfo
        )
      );
    }
  }
}

/**
 * 触发更新的函数
 */
export function trigger(
  target: object,
  type: TriggerOpTypes,
  key?: unknown,
  newValue?: unknown,
  oldValue?: unknown,
  oldTarget?: Map<unknown, unknown> | Set<unknown>
) {
  /**
   * 触发更新的函数
   */
  // 获取这个target的deps副作用集合
  const depsMap = targetMap.get(target);
  if (!depsMap) {
    // never been tracked
    // 没有被依赖收集过 直接退出
    return;
  }

  let deps: (Dep | undefined)[] = [];
  if (type === TriggerOpTypes.CLEAR) {
    // collection being cleared
    // trigger all effects for target
    deps = [...depsMap.values()];
  } else if (key === "length" && isArray(target)) {
    depsMap.forEach((dep, key) => {
      if (key === "length" || key >= (newValue as number)) {
        deps.push(dep);
      }
    });
  } else {
    // schedule runs for SET | ADD | DELETE
    if (key !== void 0) {
      deps.push(depsMap.get(key));
    }

    // also run for iteration key on ADD | DELETE | Map.SET
    switch (type) {
      case TriggerOpTypes.ADD:
        if (!isArray(target)) {
          deps.push(depsMap.get(ITERATE_KEY));
          if (isMap(target)) {
            deps.push(depsMap.get(MAP_KEY_ITERATE_KEY));
          }
        } else if (isIntegerKey(key)) {
          // new index added to array -> length changes
          deps.push(depsMap.get("length"));
        }
        break;
      case TriggerOpTypes.DELETE:
        if (!isArray(target)) {
          deps.push(depsMap.get(ITERATE_KEY));
          if (isMap(target)) {
            deps.push(depsMap.get(MAP_KEY_ITERATE_KEY));
          }
        }
        break;
      case TriggerOpTypes.SET:
        if (isMap(target)) {
          deps.push(depsMap.get(ITERATE_KEY));
        }
        break;
    }
  }

  const eventInfo = __DEV__
    ? { target, type, key, newValue, oldValue, oldTarget }
    : undefined;

  if (deps.length === 1) {
    if (deps[0]) {
      if (__DEV__) {
        triggerEffects(deps[0], eventInfo);
      } else {
        triggerEffects(deps[0]);
      }
    }
  } else {
    const effects: ReactiveEffect[] = [];
    for (const dep of deps) {
      if (dep) {
        effects.push(...dep);
      }
    }
    if (__DEV__) {
      triggerEffects(createDep(effects), eventInfo);
    } else {
      triggerEffects(createDep(effects));
    }
  }
}

export function triggerEffects(
  dep: Dep | ReactiveEffect[],
  debuggerEventExtraInfo?: DebuggerEventExtraInfo
) {
  /**
   * 触发更新的函数 执行一下这个副作用函数
   *  for of 拿到每个经过处理过的 effect函数 符合条件的情况下 执行run
   */
  // spread into array for stabilization
  for (const effect of isArray(dep) ? dep : [...dep]) {
    if (effect !== activeEffect || effect.allowRecurse) {
      if (__DEV__ && effect.onTrigger) {
        effect.onTrigger(extend({ effect }, debuggerEventExtraInfo));
      }
      if (effect.scheduler) {
        effect.scheduler();
      } else {
        /**
         * 加了很多的 边界处理
         */
        // 最重要的一步  执行 run方法 就相当于 执行  fn() 副作用函数！
        effect.run();
      }
    }
  }
}
